package obj

import (
    "fmt"
    "gitcode.com/deyiyangyang/gascheme/ast"
)

// ObjectType represents the type of an object
const (
    INTEGER_OBJ     = "INTEGER"
    REAL_OBJ        = "REAL"
    RATIONAL_OBJ    = "RATIONAL"
    BOOLEAN_OBJ     = "BOOLEAN"
    SYMBOL_OBJ      = "SYMBOL"
    LIST_OBJ        = "LIST"
    PROCEDURE_OBJ   = "PROCEDURE"
    ERROR_OBJ       = "ERROR"
    DEFINEPROC_OBJ  = "DEFINEPROC"
    STRING_OBJ      = "STRING"
    CHARACTER_OBJ   = "CHARACTER"
    VOID_OBJ        = "VOID"
    SILENT_VOID_OBJ = "SILENT_VOID"
)

// Object represents a value in the Scheme environment
// It's the interface that all Scheme values must implement
type Object interface {
    // Type returns the type of the object
    Type() ObjectType
    
    // Inspect returns a string representation of the object
    Inspect() string
}

// NoPrint is an interface for objects that should not be printed
type NoPrint interface {
    // NoPrint indicates that this object should not be printed
    NoPrint() bool
}

// Void represents a void value (unspecified return value)
type Void struct{}

// Type returns the object type as a string
func (v *Void) Type() ObjectType {
    return VOID_OBJ
}

// Inspect returns the string representation of the void object
func (v *Void) Inspect() string {
    return "#<void>"
}

// SilentVoid represents a void value that should not be printed
type SilentVoid struct{}

// Type returns the object type as a string
func (v *SilentVoid) Type() ObjectType {
    return SILENT_VOID_OBJ
}

// Inspect returns the string representation of the silent void object
func (v *SilentVoid) Inspect() string {
    return ""
}

// NoPrint indicates that this object should not be printed
func (v *SilentVoid) NoPrint() bool {
    return true
}

// DefineProcedure represents a define procedure object
type DefineProcedure struct {
    Signature *List
    Body      []ast.Expression
}

// Type returns the object type as a string
func (dp *DefineProcedure) Type() ObjectType { 
    return DEFINEPROC_OBJ 
}

// Inspect returns the string representation of the define procedure
func (dp *DefineProcedure) Inspect() string {
    return "#<define-procedure>"
}

// ObjectType is a string representing the type of an object
type ObjectType string

// Boolean represents a boolean value
type Boolean struct {
    Value bool
}

// Type returns the object type as a string
func (b *Boolean) Type() ObjectType {
    return BOOLEAN_OBJ
}

// String returns the string representation of the boolean
func (b *Boolean) Inspect() string {
    if b.Value {
        return "#t"
    }
    return "#f"
}

// Integer represents an integer value
type Integer struct {
    Value int64
}

// Type returns the object type as a string
func (i *Integer) Type() ObjectType {
    return INTEGER_OBJ
}

// String returns the string representation of the integer
func (i *Integer) Inspect() string {
    return fmt.Sprintf("%d", i.Value)
}

// Rational represents a rational number value
type Rational struct {
    Numerator   int64
    Denominator int64
}

// Type returns the object type as a string
func (r *Rational) Type() ObjectType {
    return RATIONAL_OBJ
}

// String returns the string representation of the rational number
func (r *Rational) Inspect() string {
    return fmt.Sprintf("%d/%d", r.Numerator, r.Denominator)
}

// String represents a string value
type String struct {
    Value string
}

// Type returns the object type as a string
func (s *String) Type() ObjectType {
    return STRING_OBJ
}

// Inspect returns the string representation of the string value
func (s *String) Inspect() string {
    return s.Value
}

// Character represents a character value
type Character struct {
    Value rune
}

// Type returns the object type as a string
func (c *Character) Type() ObjectType {
    return CHARACTER_OBJ
}

// Inspect returns the string representation of the character value
func (c *Character) Inspect() string {
    return "#\\" + string(c.Value)
}

// Real represents a real (floating-point) number value
type Real struct {
    Value float64
}

// Type returns the object type as a string
func (r *Real) Type() ObjectType {
    return REAL_OBJ
}

// String returns the string representation of the real number
func (r *Real) Inspect() string {
    return fmt.Sprintf("%g", r.Value)
}

// Error represents an error object
type Error struct {
    Message string
}

// Type returns the object type as a string
func (e *Error) Type() ObjectType {
    return ERROR_OBJ
}

// String returns the error message
func (e *Error) Inspect() string {
    return "ERROR: " + e.Message
}

// Symbol represents a symbol object
type Symbol struct {
    Value string
}

// Type returns the object type as a string
func (s *Symbol) Type() ObjectType {
    return SYMBOL_OBJ
}

// String returns the string representation of the symbol
func (s *Symbol) Inspect() string {
    return s.Value
}

// List represents a list object implemented as a linked list
type List struct {
    Car Object     // First element of the list
    Cdr Object     // Rest of the list or a value (for dotted pairs)
}

// Type returns the object type as a string
func (l *List) Type() ObjectType {
    return LIST_OBJ
}

// Inspect returns the string representation of the list
func (l *List) Inspect() string {
    // Handle empty list
    if l.Car == nil && l.Cdr == nil {
        return "()"
    }
    
    // Handle dotted pair - when Cdr is not a list
    if l.Cdr != nil {
        if _, isList := l.Cdr.(*List); !isList {
            // This is a dotted pair
            return "(" + l.Car.Inspect() + " . " + l.Cdr.Inspect() + ")"
        }
    }
    
    // Handle proper lists
    var result string = "("
    current := l
    first := true
    
    for current != nil {
        if current.Car == nil && current.Cdr == nil {
            // Empty list
            break
        }
        
        if !first {
            result += " "
        }
        first = false
        
        // Add the car element
        if current.Car != nil {
            result += current.Car.Inspect()
        }
        
        // Check the cdr
        if current.Cdr == nil {
            // End of list
            break
        }
        
        if cdrList, ok := current.Cdr.(*List); ok {
            // Continue with the next list node
            current = cdrList
        } else {
            // Improper list - dotted pair at the end
            if current.Cdr != nil {
                result += " . " + current.Cdr.Inspect()
            }
            break
        }
    }
    
    result += ")"
    return result
}

// ToSlice converts the linked list representation to a slice for easier manipulation
// This is primarily for testing and internal use
func (l *List) ToSlice() []Object {
    if l.Car == nil && l.Cdr == nil {
        // Empty list
        return []Object{}
    }
    
    var elements []Object
    current := l
    
    for current != nil {
        if current.Car != nil {
            elements = append(elements, current.Car)
        }
        
        if cdrList, ok := current.Cdr.(*List); ok {
            current = cdrList
        } else {
            // If Cdr is not a list, it's either nil (end of proper list) 
            // or a value (improper list)
            if current.Cdr != nil {
                // This is an improper list, add the last element
                elements = append(elements, current.Cdr)
            }
            break
        }
    }
    
    return elements
}

// Length returns the number of elements in the list
func (l *List) Length() int {
    return len(l.ToSlice())
}

// Procedure represents a user-defined procedure
type Procedure struct {
    Name   string
    Params []string
    Body   []ast.Expression
    Env    Environment
}

// Type returns the object type as a string
func (p *Procedure) Type() ObjectType {
    return PROCEDURE_OBJ
}

// String returns the string representation of the procedure
func (p *Procedure) Inspect() string {
    var result string = "(procedure " + p.Name
    if len(p.Params) > 0 {
        result += " ("
        for i, param := range p.Params {
            result += param
            if i < len(p.Params)-1 {
                result += " "
            }
        }
        result += ")"
    }
    result += ")"
    return result
}